package com.icez.base_util_library

import android.app.Activity
import android.os.Bundle
import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.navigation.NavGraph
import androidx.navigation.NavOptions
import androidx.navigation.Navigation
import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController

/**
 *  路由工具类
 * @author icez
 */
class RouteUtil {
    companion object {
        val instance: RouteUtil by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            RouteUtil()
        }
    }

    /**
     *  物理键盘返回
     * @param fragment 碎片
     * @param onBackListener 监听
     * @param isEnableBack 是否返回禁止
     */
    fun physicsBack(fragment: Fragment, onBackListener: () -> Unit, isEnableBack: Boolean = true) {
        fragment.requireActivity().onBackPressedDispatcher.addCallback(
            fragment.viewLifecycleOwner,
            object : OnBackPressedCallback(isEnableBack) {
                override fun handleOnBackPressed() {
                    onBackListener()
                }
            })
    }

    /**
     * 跳转指定的Fragment
     * @sample
     * @author Icez
     * @param fragment 当前Fragment
     * @param targetFragmentId 需要跳转的Fragment的ID
     * @param bundle 需要传输的数据
     * @param popToTargetFragmentId 从栈后面推出到指定的Fragment，并把后面不匹配的都推出
     * @param inclusive 推出到指定的栈是否把指定的Fragment也推出，默认是true；如果不推出会产生二个实例
     * @param saveState 是否保存当前与目的地的状态，默认false；暂时没有想到有什么场景可以用到
     * @param singleTop 是否把指定的Fragment推入栈顶，如果栈中有实例就不会重新去创建一个新的实例，永远只有一个实例;默认false
     * @param restoreState 推出到指定的实例，并恢复之前的状态
     * @param enterAnim 推入栈进入的动画
     * @param exitAnim 推入栈出去的动画
     * @param popEnterAnim 推出栈进入的动画
     * @param popExitAnim 推出栈出去的动画
     */
    fun navigationToFragment(
        fragment: Fragment,
        targetFragmentId: Int,
        bundle: Bundle? = null,
        popToTargetFragmentId: Int? = null,
        inclusive: Boolean = true,
        saveState: Boolean = false,
        singleTop: Boolean = false,
        restoreState: Boolean = false,
        enterAnim: Int = R.anim.nav_enter_anim,
        exitAnim: Int = R.anim.nav_exit_anim,
        popEnterAnim: Int = R.anim.nav_pop_enter_anim,
        popExitAnim: Int = R.anim.nav_pop_exit_anim,
        navGraphResId: Int? = null
    ) {
        var mTargetFragment = targetFragmentId
        if (navGraphResId != null) {
            mTargetFragment = navGraphResId
            setNavigationStartDestination(fragment, navGraphResId, targetFragmentId)
        }
        fragment.findNavController().navigate(
            mTargetFragment,
            bundle,
            getNavOptions(
                popToTargetFragmentId,
                inclusive,
                saveState,
                singleTop,
                restoreState,
                enterAnim,
                exitAnim,
                popEnterAnim,
                popExitAnim
            )
        )
    }

    /**
     * 跳转指定的Fragment
     * @sample
     * @author Icez
     * @param view 当前Fragment中的view
     * @param targetFragmentId 需要跳转的Fragment的ID
     * @param bundle 需要传输的数据
     * @param popToTargetFragmentId 从栈后面推出到指定的Fragment，并把后面不匹配的都推出
     * @param inclusive 推出到指定的栈是否把指定的Fragment也推出，默认是true；如果不推出会产生二个实例
     * @param saveState 是否保存当前与目的地的状态，默认false；暂时没有想到有什么场景可以用到
     * @param singleTop 是否把指定的Fragment推入栈顶，如果栈中有实例就不会重新去创建一个新的实例，永远只有一个实例;默认false
     * @param restoreState 推出到指定的实例，并恢复之前的状态
     * @param enterAnim 推入栈进入的动画
     * @param exitAnim 推入栈出去的动画
     * @param popEnterAnim 推出栈进入的动画
     * @param popExitAnim 推出栈出去的动画
     * @param popToTargetFragmentId 从栈后面推出到指定的Fragment，并把后面不匹配的都推出
     * @param inclusive 推出到指定的栈是否把指定的Fragment也推出，默认是true；如果不推出会产生二个实例
     * @param saveState 是否保存当前与目的地的状态，默认false；暂时没有想到有什么场景可以用到
     */
    fun navigationToFragment(
        view: View?,
        targetFragmentId: Int,
        bundle: Bundle? = null,
        popToTargetFragmentId: Int? = null,
        inclusive: Boolean = true,
        saveState: Boolean = false,
        singleTop: Boolean = false,
        restoreState: Boolean = false,
        enterAnim: Int = R.anim.nav_enter_anim,
        exitAnim: Int = R.anim.nav_exit_anim,
        popEnterAnim: Int = R.anim.nav_pop_enter_anim,
        popExitAnim: Int = R.anim.nav_pop_exit_anim,
        navGraphResId: Int? = null,
    ) {
        var mTargetFragment = targetFragmentId
        if (navGraphResId != null) {
            mTargetFragment = navGraphResId
            setNavigationStartDestination(view, navGraphResId, targetFragmentId)
        }
        view?.findNavController()?.navigate(
            mTargetFragment,
            bundle,
            getNavOptions(
                popToTargetFragmentId,
                inclusive,
                saveState,
                singleTop,
                restoreState,
                enterAnim,
                exitAnim,
                popEnterAnim,
                popExitAnim
            )
        )
    }

    /**
     * 通过xml action跳转
     * @sample
     * @author Icez
     * @param fragment 当前碎片
     * @param actionId 跳转的线条
     * @param bundle 跳转的参数
     * @param navGraphResId 跳转的navGraph id
     * @param startDestination 跳转的NavGraph 入口碎片
     */
    fun navigationXmlToFragment(
        fragment: Fragment,
        actionId: Int,
        bundle: Bundle? = null,
        navGraphResId: Int? = null,
        startDestination: Int,
        popToTargetFragmentId: Int? = null,
        inclusive: Boolean = true,
        saveState: Boolean = false,
    ) {
        if (popToTargetFragmentId != null) {
            fragment.findNavController().popBackStack(popToTargetFragmentId, inclusive, saveState)
        } else {
            setNavigationStartDestination(fragment, navGraphResId, startDestination)
            fragment.findNavController().navigate(actionId, bundle)
        }
    }

    /**
     * 推出上一页
     * @sample
     * @author Icez
     * @param fragment 当前fragment
     */
    fun navigationBack(fragment: Fragment) {
        fragment.findNavController().popBackStack()
    }

    /**
     * 推出上一页
     * @sample
     * @author Icez
     * @param fragment 当前fragment
     */
    fun navigationUp(fragment: Fragment) {
        fragment.findNavController().navigateUp()
    }

    /**
     * 推出上一页
     * @sample
     * @author Icez
     * @param view View
     */
    fun navigationBack(view: View?) {
        view?.findNavController()?.popBackStack()
    }

    /**
     * 推出上一页
     * @sample
     * @author Icez
     * @param view View
     */
    fun navigationUp(view: View?) {
        view?.findNavController()?.navigateUp()
    }

    /**
     * 设置navagation 入口
     * @sample
     * @author Icez
     * @param fragment 当前Fragment
     * @param navGraphResId 需要跳转的 graph id
     * @param startDestination 跳转的graph fragment id
     */
    fun setNavigationStartDestination(
        fragment: Fragment,
        navGraphResId: Int? = null,
        startDestination: Int? = null
    ) {
        if (navGraphResId == null || startDestination == null) {
            return
        }
        val graph = fragment.findNavController().graph
        val navGraph = graph.findNode(navGraphResId) as NavGraph
        navGraph.setStartDestination(startDestination)
    }


    /**
     * 设置navagation 入口
     * @sample
     * @author Icez
     * @param fragment 当前Fragment
     * @param navGraphResId 需要跳转的 graph id
     * @param startDestination 跳转的graph fragment id
     */
    fun setNavigationStartDestination(
        view: View?,
        navGraphResId: Int? = null,
        startDestination: Int? = null
    ) {
        if (navGraphResId == null || startDestination == null) {
            return
        }
        val graph = view?.findNavController()?.graph
        val navGraph = graph?.findNode(navGraphResId) as NavGraph
        navGraph.setStartDestination(startDestination)
    }

    /**
     * activity 设置入口
     * @sample
     * @author Icez
     * @param activity activity
     * @param navHostFragmentResId NavHost 碎片ID
     * @param navigationResId navigation xml
     * @param targetFragmentId 设置针对的碎片id
     * @param bundle 参数
     */
    fun setNavigationStartDestination(
        activity: AppCompatActivity,
        navHostFragmentResId: Int,
        navigationResId: Int,
        targetFragmentId: Int,
        bundle: Bundle? = null
    ) {
        val navHostFragment =
            activity.supportFragmentManager.findFragmentById(navHostFragmentResId) as NavHostFragment
        val navGraph = navHostFragment.navController.navInflater.inflate(navigationResId)
        navGraph.setStartDestination(targetFragmentId)
        navHostFragment.navController.setGraph(navGraph, bundle)
    }

    /**
     * 栈中是发存在Fragment
     * @sample
     * @author Icez
     * @param fragment 当前Fragment
     * @param targetFragmentId 需要查询的Fragment
     * @return true 存在Fragment; false  不存在Fragment
     */
    fun isHasFragment(fragment: Fragment, targetFragmentId: Int): Boolean {
        return !(fragment.findNavController()
            .getBackStackEntry(targetFragmentId).destination == null)
    }

    /**
     * 获取Nav的配置
     * @sample
     * @author Icez
     * @param popToTargetFragmentId 从栈后面推出到指定的Fragment，并把后面不匹配的都推出
     * @param inclusive 推出到指定的栈是否把指定的Fragment也推出，默认是true；如果不推出会产生二个实例
     * @param saveState 是否保存当前与目的地的状态，默认false；暂时没有想到有什么场景可以用到
     * @param singleTop 是否把指定的Fragment推入栈顶，如果栈中有实例就不会重新去创建一个新的实例，永远只有一个实例;默认false
     * @param restoreState 推出到指定的实例，并恢复之前的状态
     * @param enterAnim 推入栈进入的动画
     * @param exitAnim 推入栈出去的动画
     * @param popEnterAnim 推出栈进入的动画
     * @param popExitAnim 推出栈出去的动画
     */
    private fun getNavOptions(
        popToTargetFragmentId: Int? = null,
        inclusive: Boolean = true,
        saveState: Boolean = false,
        singleTop: Boolean = false,
        restoreState: Boolean = false,
        enterAnim: Int = R.anim.nav_enter_anim,
        exitAnim: Int = R.anim.nav_exit_anim,
        popEnterAnim: Int = R.anim.nav_pop_enter_anim,
        popExitAnim: Int = R.anim.nav_pop_exit_anim
    ): NavOptions {
        var mNavOptions = NavOptions.Builder()
        mNavOptions.setEnterAnim(enterAnim)
        mNavOptions.setExitAnim(exitAnim)
        mNavOptions.setPopEnterAnim(popEnterAnim)
        mNavOptions.setPopExitAnim(popExitAnim)
        if (popToTargetFragmentId != null) {
            mNavOptions.setPopUpTo(popToTargetFragmentId, inclusive, saveState)
            mNavOptions.setRestoreState(restoreState)
        }
        mNavOptions.setLaunchSingleTop(singleTop)
        return mNavOptions.build()
    }


}